Skip to content

feat: add shadow DOM support for text/input/image replacements#344

Open
svrnm wants to merge 4 commits into
mainfrom
feature/shadow-dom-support
Open

feat: add shadow DOM support for text/input/image replacements#344
svrnm wants to merge 4 commits into
mainfrom
feature/shadow-dom-support

Conversation

@svrnm

@svrnm svrnm commented Mar 17, 2026

Copy link
Copy Markdown
Owner

Summary

Resolves #146.

  • Adds shadow DOM traversal to Monkey.js so that text, input, textarea, and image replacements now apply inside open shadow roots
  • Recursively discovers nested shadow roots via querySelectorAll('*') and checks element.shadowRoot
  • Skips DemoMonkey's own live editor shadow root (dm-live-editor-host) to avoid self-interference
  • Reuses the existing _applyOnXpathGroup method with an optional contextNode parameter, using shadow-root-scoped XPath (.//text(), .//input, .//img, etc.)

Limitations

  • Only open shadow roots (mode: 'open') are accessible — closed shadow roots are a browser security boundary and cannot be reached
  • XPath evaluation with a shadow root as context node relies on Chrome's implementation

Test plan

  • Unit tests pass (234 passing, 5 new shadow DOM tests)
  • Lint and formatting clean
  • Manual testing on a page with open shadow DOM elements (e.g. web components)
  • Verify DemoMonkey's own live editor still works correctly (not affected by shadow DOM traversal)

Made with Cursor

Resolves #146. DemoMonkey now traverses open shadow roots and applies
the same replacement logic (text, input, textarea, image) inside them.
Closed shadow roots remain inaccessible by browser design.

Signed-off-by: svrnm <severin.neumann@altmuehlnet.de>
Made-with: Cursor
Copilot AI review requested due to automatic review settings March 17, 2026 18:49

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security review completed for PR #344 (feat: add shadow DOM support for text/input/image replacements).

Result: No medium/high/critical vulnerabilities found in the modified code paths.

I traced the new shadow-DOM traversal flow in Monkey.apply() through _collectOpenShadowRoots() and _applyOnXpathGroup() into Configuration.apply() sinks and did not find a new exploitable injection/authz/secret-leak/SSRF/XSS path introduced by this change.

Slack summary: PR #344 security review -> no actionable medium+ findings.

Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Shadow DOM traversal to DemoMonkey’s replacement engine so text/input/textarea/image replacements are also applied inside open shadow roots, while avoiding self-interference with the Live Editor host.

Changes:

  • Invoke shadow-root processing during Monkey.apply() via _applyOnShadowRoots.
  • Extend _applyOnXpathGroup to accept an optional contextNode for shadow-root-scoped XPath evaluation.
  • Add unit tests covering text replacement in shadow roots, nested shadow roots, skipping the live editor host, undo behavior, and shadow-root inputs.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/models/Monkey.js Adds discovery of open shadow roots and applies existing XPath-based replacements within each shadow root.
test/models/Monkey.js Adds tests validating shadow-root traversal, live editor exclusion, nested roots, undo, and input handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread test/models/Monkey.js
Comment thread src/models/Monkey.js
Comment thread src/models/Monkey.js
Comment thread src/models/Monkey.js
Comment on lines +351 to +360
sum.input += this._applyOnXpathGroup(configuration, './/input', 'input', 'value', shadowRoot)
sum.input += this._applyOnXpathGroup(
configuration,
'.//textarea',
'input',
'value',
shadowRoot
)
sum.image += this._applyOnXpathGroup(configuration, './/img', 'image', 'src', shadowRoot)
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 17, 2026 18:55
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

@svrnm I've opened a new pull request, #347, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for applying DemoMonkey replacements inside open Shadow DOM trees by discovering open shadow roots and running the existing XPath-based replacement logic within each shadow root context (while skipping DemoMonkey’s own live editor shadow root).

Changes:

  • Traverse the document (and nested shadow roots) to collect open shadowRoots and apply text/input/textarea/image replacements within them.
  • Extend _applyOnXpathGroup to optionally run XPath evaluation relative to a provided context node.
  • Add unit tests covering text replacement, nested shadow roots, undo behavior, input handling, and skipping dm-live-editor-host.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/models/Monkey.js Adds shadow root collection + shadow-root-scoped XPath application; updates XPath helper to accept a context node.
test/models/Monkey.js Adds unit tests validating replacement behavior inside open shadow roots and nested shadow roots.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread src/models/Monkey.js
Comment on lines +315 to +342
_collectOpenShadowRoots(root) {
const shadowRoots = []

// Prefer a TreeWalker-based traversal to avoid materializing a full NodeList
const ownerDocument = root.ownerDocument || root

if (ownerDocument && typeof ownerDocument.createTreeWalker === 'function') {
const walker = ownerDocument.createTreeWalker(
root,
NodeFilter.SHOW_ELEMENT,
null,
false
)

let current = walker.currentNode
while (current) {
const el = current
if (el.shadowRoot && el.id !== 'dm-live-editor-host') {
shadowRoots.push(el.shadowRoot)
shadowRoots.push(...this._collectOpenShadowRoots(el.shadowRoot))
}
current = walker.nextNode()
}

return shadowRoots
}

// Fallback for environments without TreeWalker support
Comment thread src/models/Monkey.js
Comment on lines +364 to +373
shadowRoots.push(el.shadowRoot)
shadowRoots.push(...this._collectOpenShadowRoots(el.shadowRoot))
}
}
return shadowRoots
}

_applyOnShadowRoots(configuration, sum) {
const shadowRoots = this._collectOpenShadowRoots(this.scope.document)
for (const shadowRoot of shadowRoots) {
Comment thread src/models/Monkey.js
Comment on lines +319 to +332
const ownerDocument = root.ownerDocument || root

if (ownerDocument && typeof ownerDocument.createTreeWalker === 'function') {
const walker = ownerDocument.createTreeWalker(
root,
NodeFilter.SHOW_ELEMENT,
null,
false
)

let current = walker.currentNode
while (current) {
const el = current
if (el.shadowRoot && el.id !== 'dm-live-editor-host') {
…dImage selectors (#347)

Co-authored-by: svrnm <1519757+svrnm@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Change content in Shadow DOM

3 participants